#ifndef VSMLMODEL_H
#define VSMLMODEL_H

#include <QSize>
#include <QAbstractListModel>
#include <QMap>
#include <QList>
#include <QLinkedList>
#include <QStringList>

class vsMLDelegate;


namespace mlData {
    typedef enum : int {
        id = 0, name, artist, path, status, size, mime, uprate, dlrate, seeded, requests, duration,
        END
    } type;
}

QString getStringForMLType(mlData::type t);
QString getTypeStringForMLType(mlData::type t);
mlData::type getMLTypeForTypeString(const QString &idS); // reverse from above, returns "string'ed" mlData enum


struct vsMLRow
{
    QStringList values;

    qint64 fileSize      = 0;
    qint64 seeded        = 0;
    int    mediaDuration = 0;
    int    requests      = 0;

    vsMLRow(const QString &id,   const QString &name,   const QString &artist,
            const QString &path, const QString &status, const QString &size,
            const QString &mime, const QString &duration,
            qint64 sizeBytes, int durationSecs) : fileSize(sizeBytes), mediaDuration(durationSecs)
    {
        values << id << name << artist << path << status << size << mime
               << /*uprate*/ "" << /*dlrate*/ "" << /*seeded*/ "" << /*requests*/ ""
               << duration;
    }

    bool operator==(const vsMLRow &other) { return values[mlData::id] == other.values[mlData::id]; }
};


struct vsMLHeader
{
    mlData::type  type  = mlData::END;
    QString       str   = "INVALID";
    int           width = 100;

    //vsMLHeader() {} // map needs a default ctor
    vsMLHeader(mlData::type t, const QString &s, int w = 100) : type(t), str(s), width(w) {}
    vsMLHeader(const vsMLHeader &other) : type(other.type), str(other.str), width(other.width) {}

    bool operator==(const vsMLHeader &other) { return type == other.type; }
};


class vsMLModel : public QAbstractListModel
{
    Q_OBJECT
    friend class vsMLDelegate;

public:
    explicit vsMLModel(QObject *parent = 0);
    ~vsMLModel();

    int rowCount   (const QModelIndex &/*parent*/) const override { return rows.size(); }
    int columnCount(const QModelIndex &/*parent*/) const override { return sortedHeaders.size(); }

    QVariant data       (const QModelIndex &index, int role)                 const override;
    QVariant headerData (int section, Qt::Orientation orientation, int role) const override;
    QMap<int, QVariant> itemData(const QModelIndex &index)                   const override;

    bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
    bool setItemData(const QModelIndex &index, const QMap<int, QVariant> &roles)           override;
    bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &value,
                           int role = Qt::EditRole)                                        override;

    Qt::ItemFlags   flags(const QModelIndex &index)          const override;
    QStringList     mimeTypes()                              const override;
    QMimeData*      mimeData(const QModelIndexList &indexes) const override;
    Qt::DropActions supportedDropActions()                   const override;

    bool dropMimeData(const QMimeData *data, Qt::DropAction action,
                      int row, int column, const QModelIndex &parent) override;

    void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override;

    bool hasMedia(const QString &id) { return mappedRows.contains(id); }
    void addMedia(const vsMLRow &r);
    void updateMedia(const QString &id, const QMap<mlData::type, QVariant> values);
    bool removeMedia(const QString &id);
    bool changeMediaID(const QString &oldID, const QString &newID);

    void clearHeaderData ();
    void rearrangeHeaders(const QStringList &typeStrings);
    void resizeHeaders   (const QList<int> &widths);

    QStringList headerTypeStrings();
    QList<int>  headerSizes();
    QStringList mediaIDList() const;
    QStringList valuesOfRows(const QList<int> &rows, mlData::type t) const;

    void setRowHeight(int rh) { rowHeight = rh; }    

public slots:
    void onMLHeaderResized  (int i, int newS, int totalW);
    void onMLHeaderReordered(int i, int newI);

    void filter(const QString &f); // set filter to search through list

private:
    Q_DISABLE_COPY(vsMLModel)

    QLinkedList<vsMLRow>    rows;
    QMap<QString, vsMLRow*> mappedRows; // for fast access by key
    QList<vsMLRow*>         sortedRows; // for fast access by column index

    bool            filtering = false;
    QList<vsMLRow*> filteredMatch;
    QList<vsMLRow*> filteredRest;

    QList<vsMLHeader>          headers; // logical order for QHeaderView
    QMap<QString, vsMLHeader*> mappedHeaders;
    QList<vsMLHeader*>         sortedHeaders;

    int headerTotalWidth = 100;
    int rowHeight        = 20;

    inline void redrawEverything()
    {
        const QModelIndex &first = createIndex(0,                     0),
                          &last  = createIndex(sortedRows.size() - 1, 0);

        emit dataChanged(first, last);
    }
};


#endif // VSMLMODEL_H
